#include "CentralCache.h"
#include "PageCache.h"

// 静态成员需要在cpp初始化，最好不要在.h文件初始化
CentralCache CentralCache::_Inst;

// 从SpanList中获取一个Span // 调用前一定是加锁的
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	// 从SpanList中找的一个非空的span，
	Span* it = list.Begin();
	while (it != list.End())
	{
		if (it->_list)
		{
			return it;
		}
		it = it->_next;
	}

	list._mtx.unlock(); // 先释放 桶锁
	// 发现没有对应的span，从PageCache中获取

	PageCache::GetInstance()->_page_mtx.lock(); // 上页锁
	//cout << "get span from page_cache: size " << size << " _n: " << ClassSize::Nummovepage(size) << endl;
	Span* span = PageCache::GetInstance()->GetSpan(ClassSize::Nummovepage(size));
	PageCache::GetInstance()->_page_mtx.unlock(); // 释放页锁
	// span是独立的，现在不属于任何cache，可以不加锁使用
	// 连接起来对应的list
	char* start = (char*)(span->_pageid << PAGE_SHIFT);
	char* end = start + (span->_n << PAGE_SHIFT);
    // std::cout << "从页缓存申请到内存，进行切分" << std::endl;
	span->_blocksize = size;
	// 方式：尾插
	span->_list = start;
	void* tail = span->_list;
	start += span->_blocksize;
	while (start < end)
	{
		NextBlock(tail) = start; // bug位置
		start += span->_blocksize;
		tail = NextBlock(tail);
	}
	NextBlock(tail) = nullptr;	// 尾置空

    // std::cout << "切分完毕" << std::endl;
	list._mtx.lock();			// 桶上锁
	list.PushFront(span);		// 插入到链表

	return span;
}

// 从CentralCache获取范围的块（大小size，批量个数BatchNum），返回实际给的块个数(ActuralNum)
size_t CentralCache::FetchRangeBlock(void*& start, void*& end, size_t BatchNum, size_t size)
{
	assert(BatchNum);

	size_t index = ClassSize::Index(size);
	size_t alignsize = ClassSize::RoundUp(size);

	_spanlist[index]._mtx.lock(); // 加锁
	
	Span* span = GetOneSpan(_spanlist[index], alignsize); // 拿到span
	assert(span && span->_list);

	// 预期从span中拿BatchNum个block
	// 实际能拿多少拿多少，ActuralNum个 方式：掐头
	start = end = span->_list;
	size_t ActuralNum = 1;
	while (ActuralNum < BatchNum && NextBlock(end))
	{
		end = NextBlock(end);
		ActuralNum++;
		span->_usecount++;
	}
	span->_usecount++;
	span->_list = NextBlock(end);
	NextBlock(end) = nullptr; // 处理返回的链表的尾置空

	_spanlist[index]._mtx.unlock(); // 解锁
	return ActuralNum;
}

// 释放链表到对应的span
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = ClassSize::Index(size);
	SpanList& list = CentralCache::GetInstance()->_spanlist[index]; // 找到centralcache对应的桶

	list._mtx.lock(); // 下面是对桶的操作，需要加锁

	while (start)
	{
		void* next = NextBlock(start);
		Span* span = PageCache::GetInstance()->MapBlockToSpan(start);
		NextBlock(start) = span->_list;
		span->_list = start;
		start = next;

		// 如果usecount为0，表示页已经完整，向pagecache回收
		span->_usecount--;
		if (span->_usecount == 0)
		{
			list.Ereas(span);

			list._mtx.unlock(); // 下面是对pagecache的操作，可以先解锁

			PageCache::GetInstance()->_page_mtx.lock(); // 上page锁
			span->_isUse = false; // 修改isuse属性必须要加锁
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->_page_mtx.unlock();

			list._mtx.lock();
		}
	}

	list._mtx.unlock();
}

